/**  
 * All rights Reserved, Designed By www.shatayy.com
 * @Title:  ${file_name}   
 * @Package ${package_name}   
 * @Description:    ${todo}(用一句话描述该文件做什么)   
 * @author: 宁波沙塔信息技术有限公司     
 * @date:   ${date} ${time}   
 * @version V1.0 
 * @Copyright: ${year} www.tydic.com Inc. All rights reserved. 
 * 注意：本内容仅限于宁波沙塔信息技术股份有限公司内部传阅，禁止外泄以及用于其他的商业目
 */
package com.nbst.comnutil;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStreamWriter;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.net.InetAddress;
import java.net.URL;
import java.net.URLConnection;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import com.alibaba.fastjson.JSON;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;

import net.sourceforge.pinyin4j.PinyinHelper;
import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType;
import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;
import net.sourceforge.pinyin4j.format.HanyuPinyinToneType;
import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination;

public class Tools {
	private static int DEF_DIV_SCALE = 10; // 默认除法运算精度

	public static Map<String, HttpSession> SESSIONS = new HashMap<String, HttpSession>(10); // 用户登录会话集合

	private static final Logger logger = LoggerFactory.getLogger(Tools.class);

	private static HashMap<String, Lock> lockMap = new HashMap<>();

	// 利用Introspector和PropertyDescriptor 将Bean --> Map
	public static Map<String, Object> transBean2Map(Object obj) {

		if (obj == null) {
			return null;
		}
		Map<String, Object> map = new HashMap<String, Object>();
		try {
			BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
			PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
			for (PropertyDescriptor property : propertyDescriptors) {
				String key = property.getName();

				// 过滤class属性
				if (!key.equals("class")) {
					// 得到property对应的getter方法
					Method getter = property.getReadMethod();
					if (getter != null) {
						Object value = getter.invoke(obj);
						if (value != null) {
							if (value.equals("")) {
								value = null;
							}
						}
						if (value != null) {
							map.put(key, value);
						}
					}
				}

			}
		} catch (Exception e) {
			// System.out.println("transBean2Map Error " + e);
			logger.error(obj.getClass().toString());
			logger.error("transBean2Map Error " + e);
		}
		return map;
	}

	/**
	 * 
	 * @Title: 上传图片 @param @param str File @param @return @return String @throws
	 */
	public static String uploadFile(MultipartFile uploadImg, String path) {
		BufferedOutputStream stream = null;
		if ((uploadImg != null) && !uploadImg.isEmpty()) {
			try {
				String uploadFilePath = uploadImg.getOriginalFilename();
				// System.out.println("uploadFlePath:" + uploadFilePath);
				// 截取上传文件的文件名
				String uploadFileName = uploadFilePath.substring(uploadFilePath.lastIndexOf('\\') + 1,
						uploadFilePath.indexOf('.'));
				// System.out.println("multiReq.getFile()" + uploadFileName);
				// 截取上传文件的后缀
				String uploadFileSuffix = uploadFilePath.substring(uploadFilePath.indexOf('.') + 1,
						uploadFilePath.length());
				// System.out.println("uploadFileSuffix:" + uploadFileSuffix);
				/*
				 * path = "//uploadFiles//" + uploadFileName + "." + uploadFileSuffix;
				 */
				File dest = new File(path);
				if (!dest.getParentFile().exists()) {
					dest.getParentFile().mkdirs();
				}
				stream = new BufferedOutputStream(new FileOutputStream(dest));
				byte[] bytes = uploadImg.getBytes();

				stream.write(bytes, 0, bytes.length);
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				try {
					if (stream != null) {
						stream.close();
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return path.replace("//", "/");
	}

	// @描述：是否是2003的excel，返回true是2003
	public static boolean isExcel2003(String filePath) {
		return filePath.matches("^.+\\.(?i)(xls)$");
	}

	// @描述：是否是2007的excel，返回true是2007
	public static boolean isExcel2007(String filePath) {
		return filePath.matches("^.+\\.(?i)(xlsx)$");
	}

	/**
	 * 验证EXCEL文件
	 * 
	 * @param filePath
	 * @return
	 */
	public static boolean validateExcel(String filePath) {
		if ((filePath == null) || !(isExcel2003(filePath) || isExcel2003(filePath))) {
			return false;
		}
		return true;
	}

	/**
	 * 获取汉字串拼音首字母，英文字符不变
	 * 
	 * @param chinese
	 *            汉字串
	 * @return 汉语拼音首字母
	 */
	public static String cn2FirstSpell(String chinese) {
		StringBuffer pybf = new StringBuffer();
		char[] arr = chinese.toCharArray();
		HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();
		defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);
		defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
		for (char element : arr) {
			if (element > 128) {
				try {
					String[] _t = PinyinHelper.toHanyuPinyinStringArray(element, defaultFormat);
					if (_t != null) {
						pybf.append(_t[0].charAt(0));
					}
				} catch (BadHanyuPinyinOutputFormatCombination e) {
					e.printStackTrace();
				}
			} else {
				pybf.append(element);
			}
		}
		return pybf.toString().replaceAll("\\W", "").trim().toUpperCase();
	}

	/**
	 * 获取汉字串拼音，英文字符不变
	 * 
	 * @param chinese
	 *            汉字串
	 * @return 汉语拼音
	 */
	public static String cn2Spell(String chinese) {
		StringBuffer pybf = new StringBuffer();
		char[] arr = chinese.toCharArray();
		HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();
		defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);
		defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
		for (char element : arr) {
			if (element > 128) {
				try {
					pybf.append(PinyinHelper.toHanyuPinyinStringArray(element, defaultFormat)[0]);
				} catch (BadHanyuPinyinOutputFormatCombination e) {
					e.printStackTrace();
				}
			} else {
				pybf.append(element);
			}
		}
		return pybf.toString();
	}

	/**
	 * 向指定URL发送GET方法的请求
	 * 
	 * @param url
	 *            发送请求的URL
	 * @param param
	 *            请求参数，请求参数应该是 name1=value1&name2=value2 的形式。
	 * @return URL 所代表远程资源的响应结果
	 */
	public static String sendGet(String url, String param) {
		String result = "";
		BufferedReader in = null;
		try {
			String urlNameString = url + "?" + param;
			URL realUrl = new URL(urlNameString);
			// 打开和URL之间的连接
			URLConnection connection = realUrl.openConnection();
			// 设置通用的请求属性
			connection.setRequestProperty("accept", "*/*");
			connection.setRequestProperty("connection", "Keep-Alive");
			connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
			connection.setRequestProperty("Accept-Charset", "utf-8");
			connection.setRequestProperty("contentType", "utf-8");
			// 建立实际的连接
			connection.connect();
			// 获取所有响应头字段
			Map<String, List<String>> map = connection.getHeaderFields();
			// 遍历所有的响应头字段
			for (String key : map.keySet()) {
				System.out.println(key + "--->" + map.get(key));
			}
			// 定义 BufferedReader输入流来读取URL的响应
			in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
			String line;
			while ((line = in.readLine()) != null) {
				result += line;
			}
		} catch (Exception e) {
			System.out.println("发送GET请求出现异常！" + e);
			e.printStackTrace();
		}
		// 使用finally块来关闭输入流
		finally {
			try {
				if (in != null) {
					in.close();
				}
			} catch (Exception e2) {
				e2.printStackTrace();
			}
		}
		System.out.println(result);
		return result;
	}

	/**
	 * 向指定 URL 发送POST方法的请求
	 * 
	 * @param url
	 *            发送请求的 URL
	 * @param param
	 *            请求参数，请求参数应该是 name1=value1&name2=value2 的形式。
	 * @return 所代表远程资源的响应结果
	 */
	public static String sendPost(String url, String param) {
		OutputStreamWriter out = null;
		BufferedReader in = null;
		String result = "";
		try {
			URL realUrl = new URL(url);
			// 打开和URL之间的连接
			URLConnection conn = realUrl.openConnection();
			// 设置通用的请求属性
			conn.setRequestProperty("accept", "*/*");
			conn.setRequestProperty("connection", "Keep-Alive");
			conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
			conn.setRequestProperty("Accept-Charset", "utf-8");
			conn.setRequestProperty("contentType", "utf-8");
			// 发送POST请求必须设置如下两行
			conn.setDoOutput(true);
			conn.setDoInput(true);
			// 获取URLConnection对象对应的输出流
			out = new OutputStreamWriter(conn.getOutputStream(), "utf-8");
			// 发送请求参数
			out.write(param);
			// flush输出流的缓冲
			out.flush();
			// 定义BufferedReader输入流来读取URL的响应
			in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
			String line;
			while ((line = in.readLine()) != null) {
				result += line;
			}
		} catch (Exception e) {
			System.out.println("发送 POST 请求出现异常！" + e);
			e.printStackTrace();
		}
		// 使用finally块来关闭输出流、输入流
		finally {
			try {
				if (out != null) {
					out.close();
				}
				if (in != null) {
					in.close();
				}
			} catch (IOException ex) {
				ex.printStackTrace();
			}
		}
		System.out.println(result);
		return result;
	}

	/**
	 * 
	 * 提供精确的加法运算。
	 * 
	 * @param v1
	 *            被加数
	 * @param v2
	 *            加数
	 * @return 两个参数的和
	 * @author 兵
	 * @date 2018-6-9
	 */
	public static double add(double v1, double v2) {
		BigDecimal b1 = new BigDecimal(Double.toString(v1));
		BigDecimal b2 = new BigDecimal(Double.toString(v2));
		return b1.add(b2).doubleValue();
	}

	/**
	 * 
	 * 提供精确的减法运算。
	 * 
	 * @param v1
	 *            被减数
	 * @param v2
	 *            减数
	 * @return 两个参数的差
	 * @author 兵
	 * @date 2018-6-9
	 */
	public static double sub(double v1, double v2) {
		BigDecimal b1 = new BigDecimal(Double.toString(v1));
		BigDecimal b2 = new BigDecimal(Double.toString(v2));
		return b1.subtract(b2).doubleValue();
	}

	/**
	 * 
	 * 提供精确的乘法运算。
	 * 
	 * @param v1
	 *            被乘数
	 * @param v2
	 *            乘数
	 * @return 两个参数的积
	 * @author 兵
	 * @date 2018-6-9
	 */
	public static double mul(double v1, double v2) {
		BigDecimal b1 = new BigDecimal(Double.toString(v1));
		BigDecimal b2 = new BigDecimal(Double.toString(v2));
		return b1.multiply(b2).doubleValue();
	}

	/**
	 * 
	 * 提供精确的乘法运算。
	 * 
	 * @param v1
	 *            被乘数
	 * @param v2
	 *            乘数
	 * @param v3
	 *            乘数
	 * @return double
	 * @author 兵
	 * @date 2018-6-9
	 */
	public static double mul(double v1, double v2, double v3) {
		BigDecimal b1 = new BigDecimal(Double.toString(v1));
		BigDecimal b2 = new BigDecimal(Double.toString(v2));
		BigDecimal b3 = new BigDecimal(Double.toString(v3));
		return b1.multiply(b2).multiply(b3).doubleValue();
	}

	/**
	 * 
	 * 提供（相对）精确的除法运算，当发生除不尽的情况时，精确到 小数点以后10位，以后的数字四舍五入。
	 * 
	 * @param v1
	 *            被除数
	 * @param v2
	 *            除数
	 * @return 两个参数的商
	 * @author 兵
	 * @date 2018-6-9
	 */
	public static double div(double v1, double v2) {
		return div(v1, v2, DEF_DIV_SCALE);
	}

	/**
	 * 提供（相对）精确的除法运算。当发生除不尽的情况时，由scale参数指 定精度，以后的数字四舍五入。
	 * 
	 * @param v1
	 *            被除数
	 * @param v2
	 *            除数
	 * @param scale
	 *            表示表示需要精确到小数点以后几位。
	 * @return 两个参数的商
	 */
	public static double div(double v1, double v2, int scale) {
		if (scale < 0) {
			throw new IllegalArgumentException("The scale must be a positive integer or zero");
		}
		BigDecimal b1 = new BigDecimal(Double.toString(v1));
		BigDecimal b2 = new BigDecimal(Double.toString(v2));
		return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
	}

	/**
	 * 调用cmd命令。
	 * 
	 * @param cmd
	 *            命令
	 * 
	 * @return 执行结果
	 */
	public static String execcmd(String cmd) {
		Runtime run = Runtime.getRuntime();
		String szline = null;
		StringBuilder sb = new StringBuilder();
		try {
			// run.exec("cmd /k shutdown -s -t 3600");
			Process process = run.exec(cmd);

			InputStream input = process.getInputStream();
			BufferedReader reader = new BufferedReader(new InputStreamReader(input, "GBK"));
			while ((szline = reader.readLine()) != null) {
				sb.append(szline + "\n");
			}
			reader.close();
			process.waitFor();
			process.destroy();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return sb.toString();
	}

	/**
	 * 返回登录信息失效信息
	 * 
	 * @return NormalResult
	 * @author 兵
	 * @date 2018-4-16
	 */
	public static NormalResult logError() {
		return new NormalResult("9999", "登录信息已失效，请重新登录");
	}

	/**
	 * 根据起始行数与每页显示条数返回页码
	 * 
	 * @param offset
	 * @param row
	 * @return int
	 * @author King
	 * @date 2018年11月9日
	 */
	public static int getPageNum(int offset, int row) {
		return (offset / row) + 1;
	}

	/**
	 * 把字符串转换成bean对象
	 * 
	 * @param str
	 * @param clazz
	 * @return T
	 * @author King
	 * @date 2018年11月13日
	 */
	public static <T> T stringToBean(String str, Class<T> clazz) {
		if ((str == null) || (str.length() <= 0) || (clazz == null)) {
			return null;
		}
		if ((clazz == int.class) || (clazz == Integer.class)) {
			return (T) Integer.valueOf(str);
		} else if (clazz == String.class) {
			return (T) str;
		} else if ((clazz == long.class) || (clazz == Long.class)) {
			return (T) Long.valueOf(str);
		} else {
			return JSON.toJavaObject(JSON.parseObject(str), clazz);
		}
	}

	/**
	 * 比较传入的str是否符合regex规则（以regex开头）
	 * 
	 * @param str
	 * @param regex
	 * @return boolean
	 * @author King
	 * @date 2019年3月30日
	 */
	public static boolean regexLookingAt(String str, String regex) {
		Pattern p = Pattern.compile(regex);
		Matcher m = p.matcher(str);
		return m.lookingAt();
	}

	/**
	 * 获取某个文件夹下的所有文件
	 *
	 * @param fileNameList
	 *            存放文件名称的list
	 * @param path
	 *            文件夹的路径
	 * @return
	 */
	public static ArrayList<String> getAllFileName(String path) {
		ArrayList<String> files = new ArrayList<String>();
		// boolean flag = false;
		File file = new File(path);
		File[] tempList = file.listFiles();

		for (File element : tempList) {
			if (element.isFile()) {
				// System.out.println("文 件：" + tempList[i]);
				// fileNameList.add(tempList[i].toString());
				files.add(element.getName());
			}
			if (element.isDirectory()) {
				// System.out.println("文件夹：" + tempList[i]);
				getAllFileName(element.getAbsolutePath());
			}
		}
		return files;
	}

	// 深拷贝
	public static <T extends Serializable> T clone(T obj) {
		T clonedObj = null;
		try {
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			ObjectOutputStream oos = new ObjectOutputStream(baos);
			oos.writeObject(obj);
			oos.close();
			ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
			ObjectInputStream ois = new ObjectInputStream(bais);
			clonedObj = (T) ois.readObject();
			ois.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return clonedObj;
	}

	/**
	 * 获取用户真实IP地址，不使用request.getRemoteAddr();的原因是有可能用户使用了代理软件方式避免真实IP地址, 参考文章：
	 * http://developer.51cto.com/art/201111/305181.htm
	 * 
	 * 可是，如果通过了多级反向代理的话，X-Forwarded-For的值并不止一个，而是一串IP值，究竟哪个才是真正的用户端的真实IP呢？
	 * 答案是取X-Forwarded-For中第一个非unknown的有效IP字符串。
	 * 
	 * 如：X-Forwarded-For：192.168.1.110, 192.168.1.120, 192.168.1.130, 192.168.1.100
	 * 
	 * 用户真实IP为： 192.168.1.110
	 * 
	 * @param request
	 * @return String
	 * @author King
	 * @date 2019年7月9日
	 */
	public static String getIP() {
		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
				.getRequest();
		String ipAddress = request.getHeader("x-forwarded-for");
		if ((ipAddress == null) || (ipAddress.length() == 0) || "unknown".equalsIgnoreCase(ipAddress)) {
			ipAddress = request.getHeader("Proxy-Client-IP");
		}
		if ((ipAddress == null) || (ipAddress.length() == 0) || "unknown".equalsIgnoreCase(ipAddress)) {
			ipAddress = request.getHeader("WL-Proxy-Client-IP");
		}
		if ((ipAddress == null) || (ipAddress.length() == 0) || "unknown".equalsIgnoreCase(ipAddress)) {
			ipAddress = request.getRemoteAddr();
			if (ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")) {
				// 根据网卡取本机配置的IP
				InetAddress inet = null;
				try {
					inet = InetAddress.getLocalHost();
				} catch (UnknownHostException e) {
					e.printStackTrace();
				}
				ipAddress = inet.getHostAddress();
			}
		}
		// 对于通过多个代理的情况，第一个IP为客户端真实IP,多个IP按照','分割
		if ((ipAddress != null) && (ipAddress.length() > 15)) { // "***.***.***.***".length() = 15
			if (ipAddress.indexOf(",") > 0) {
				ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
			}
		}
		return ipAddress;
	}

	public static String getLocalIP() throws UnknownHostException {
		String localIP = "";
		InetAddress addr = InetAddress.getLocalHost();

		localIP = addr.getHostAddress().toString();
		return localIP;
	}

	/**
	 * 判断传入的数据是否为整数
	 * 
	 * @param value
	 * @return boolean
	 * @author King
	 * @date 2019年11月8日
	 */
	public static boolean isInteger(Double value) {
		if (value != null) {
			String srt = value.toString();
			String str[] = srt.split("\\.");
			if (str.length == 1) {
				return true;
			} else if (str.length == 2) {
				if (Double.valueOf(str[1]) == 0) {
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * 通用的分页查询方法
	 * 
	 * @param map
	 * @param offset
	 * @param row
	 * @return Page
	 * @author King
	 * @date 2020年1月10日
	 */
	public static Page currencyQuery(Map<String, Object> map, Integer offset, Integer row) {
		if (offset == null) {
			offset = 1;
		}
		if (row == null) {
			row = 1000000;
		}
		Page page = null;
		try {
			String orderBy = null;
			if (map != null) {
				if (map.get("orderBy") != null) {
					orderBy = map.get("orderBy").toString();
				}
			}
			if (StringUtil.isEmpty(orderBy)) {
				page = PageHelper.startPage(getPageNum(offset, row), row);
			} else {
				page = PageHelper.startPage(getPageNum(offset, row), row, orderBy);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return page;
	}

	/**
	 * 通用的分页查询方法
	 * 
	 * @param object
	 * @param offset
	 * @param row
	 * @return Page
	 * @author King
	 * @date 2020年1月10日
	 */
	public static Page currencyQuery(Object object, Integer offset, Integer row) {
		if (offset == null) {
			offset = 1;
		}
		if (row == null) {
			row = 1000000;
		}
		Page page = PageHelper.startPage(getPageNum(offset, row), row);
		;
		return page;
	}

	/**
	 * javaBean转Map
	 * 
	 * @param condition
	 * @return Map<String,Object>
	 * @author King
	 * @date 2020年2月18日
	 */
	public static Map<String, Object> beanToMap(Object condition) {
		if (condition == null) {
			return null;
		}
		if (condition instanceof Map) {
			return (Map<String, Object>) condition;
		}
		Map<String, Object> objectAsMap = new HashMap<String, Object>();
		BeanInfo info = null;
		try {
			info = Introspector.getBeanInfo(condition.getClass());
		} catch (IntrospectionException e) {
			e.printStackTrace();
		}

		for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
			Method reader = pd.getReadMethod();
			if ((reader != null) && !"class".equals(pd.getName())) {
				try {
					objectAsMap.put(pd.getName(), reader.invoke(condition));
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
		return objectAsMap;
	}

	/**
	 * 取得锁对象
	 *
	 * @param id
	 */
	public static Lock getLock(String id) {
		Lock lock = lockMap.get(id);
		if (lock == null) {
			synchronized (Tools.class) {
				lock = lockMap.get(id);
				if (lock == null) {
					// System.out.println("key:" + id + " 新建锁");
					lock = new ReentrantLock();
					lockMap.put(id, lock);
					// System.out.println("key:" + id + " 新建锁完成");
				}
			}
		}
		return lock;
	}

}